/*
 * FiguraClase.java
 * 
 * Copyright (C) 2008 David Castellanos Serrano
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package dpoo.figuras;

import dpoo.figuras.estadoclase.EstadoClaseNormal;
import dpoo.figuras.estadoclase.EstadoClase;
import dpoo.control.Control;

import java.util.Vector;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import javax.swing.*;

/**
 *
 * @author david
 */
public class FiguraClase extends JComponent implements Subject, MouseListener, MouseMotionListener {

    private Vector<Metodo> metodos;
    private Vector<Atributo> atributos;

    //PATRÓN STATE
    private EstadoClase mEstadoClase = new EstadoClaseNormal();
    
    //PATRÓN SINGLETON
    private Control mControl = Control.instance();
    
    //PATRÓN OBSERVER
    private Vector<Observer> observers = new Vector<Observer>();
    
    private int x, y, w, h;
    private String nombre;
    private ModVisi modVisi;
    
    
    public FiguraClase(int x, int y) {
        this.x = x;
        this.y = y;
        this.w = 100;
        this.h = 70;
        
        this.nombre = "NuevaClase";
        this.modVisi = new ModVisi(ModVisi.PUBLICO, false, false, false);
        
        this.metodos = new Vector<Metodo>();
        this.atributos = new Vector<Atributo>();
        
        this.setBounds(this.x, this.y, this.w, this.h);
        this.addMouseListener(this); 
        this.addMouseMotionListener(this);
    }

    
    public void updateBounds() {
        int height = 20; //espacio para el nombre de la clase
        height += (12 * this.atributos.size()) + 3;  //espacio para los atributos
        height += (12 * this.metodos.size()) + 3;    //espacio para los métodos
        
        this.h = height;
        this.setBounds(this.getX(), this.getY(), this.w, this.h);
    }
    
    
    @Override
    @SuppressWarnings("static-access")
    public void paint(Graphics g) {
        this.updateBounds();
        this.mEstadoClase.paint(g, this);
        
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        //nombre de la clase
        Font font = new Font("Default", Font.BOLD, 12);
        g2.setFont(font);
        g2.drawString(this.nombre, 3, 15);
        
        
        //atributos
        int desplaY = 20;
        font = new Font("Default", Font.PLAIN, 10);
        g2.setFont(font);
        for(Atributo atributo : this.atributos) {
            desplaY += 12;
            g2.drawString(Atributo.tipo2String(atributo.getTipo()) + " " + atributo.getNombre(), 3, desplaY);
        }
        
        g2.setStroke(new BasicStroke(1.0f));
        g2.draw(new Rectangle2D.Double(0, 20, w-1, 12 * this.atributos.size() + 2));
        
        //metodos
        desplaY = 20 + (12 * this.atributos.size() + 2);
        for(Metodo metodo : this.metodos) {
            desplaY += 12;
            g2.drawString(Atributo.tipo2String(metodo.getTipoDevuelto()) + " " + metodo.getNombre() + "()", 3, desplaY);
        }
        
        g2.setStroke(new BasicStroke(1.0f));
        g2.draw(new Rectangle2D.Double(0, 20, w-1, 12 * this.atributos.size() + 2));
    }
    
    
    @Override
    public void repaint() {
        super.repaint();
        this.notifyObservers();
    }
    
    
    //PATRÓN STATE
    /**
     * Devuelve el estado interno de la FiguraClase
     * @return
     */
    public EstadoClase getEstado() {
        return this.mEstadoClase;
    }
    
    /**
     * Configura el estado interno de la FiguraClase
     * @param foo
     */
    public void setEstado(EstadoClase foo) {
        this.mEstadoClase = foo;
    }
    
    
    
    //PATRÓN OBSERVER
    /**
     * Añade un observador a la lista de observadores del estado de la FiguraClase
     * @param o
     */
    public void attach(Observer o) {
        this.observers.add(o);
    }
    
    /**
     * Elimina un observador de la lista de observadores del estado de la FiguraClase
     * @param o
     */
    public void detach(Observer o) {
        this.observers.remove(o);
    }
    
    /**
     * Notifica a los observadores del estado de la FiguraClase
     */
    public void notifyObservers() {
        Iterator iterador = this.observers.iterator();
        Observer observador;
        while(iterador.hasNext()) {
            observador = (Observer) iterador.next();
            observador.update();
        }
    }
    
    
    
    //MÉTODOS PARA DEVOLVER INFORMACIÓN DE LA CLASE
    /**
     * Devuelve el nombre de la clase
     * @return
     */
    public String getNombre() {
        return this.nombre;
    }
    
    /**
     * Devuelve el estado de los modificadores y la visibilidad de la FiguraClase
     * @return
     */
    public ModVisi getModVisi() {
        return this.modVisi;
    }
    
    /**
     * Devuelve los atributos de la FiguraClase
     * @return
     */
    public Vector<Atributo> getAtributos () {
        return atributos;
    }

    /**
     * Devuelve los métodos de la FiguraClase
     * @return
     */
    public Vector<Metodo> getMetodos () {
        return metodos;
    }
    
    //MÉTODOS PARA MANIPULAR INFORMACIÓN DE LA CLASE
    /**
     * Fija el nombre de la clase
     * @param nombre
     */
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    
    /**
     * Añade un método a la lista de métodos de la FiguraClase
     * @param metodo
     */
    public void addMetodo(Metodo metodo) {
        this.metodos.add(metodo);
    }
    
    
    //EVENTOS DEL RATÓN
    public void mousePressed(MouseEvent event) {}
    public void mouseReleased(MouseEvent event) {}
    public void mouseMoved(MouseEvent event) {}
    public void mouseClicked(MouseEvent event) {
        this.mControl.claseMouseClicked(this);
    }
    
    public void mouseEntered(MouseEvent event) {
        this.mControl.claseMouseEntered(this);
    }
    
    public void mouseExited(MouseEvent event) {
        this.mControl.claseMouseExited(this);
    }

    //funciones del interface MouseMotionListener
    public void mouseDragged(MouseEvent event) {
        //actualizo la posición de la clase en el tapiz
        this.setBounds(this.getX()+event.getX(), this.getY()+event.getY(), this.w, this.h);
        
        //PATRÓN OBSERVER: notifica a las FiguraHerencia relacionadas con él para que se repinten
        this.notifyObservers();
    }
    
    
    //GENERACIÓN DE CÓDIGO
    /**
     * Devuelve una representación como String del objeto FiguraClase
     * @return
     */
    @Override
    public String toString() {
        String foo = "";
        
        foo += this.modVisi.toString() + " " + this.nombre + "{\n";
        
        foo += "//ATRIBUTOS\n";
        for(Atributo atributo : this.atributos) {
            foo += atributo.toString() + ";\n";
        }
        
        foo += "//METODOS\n";
        for(Metodo metodo : this.metodos) {
            foo += metodo.toString() + ";\n";
        }
        
        foo += "\n}";
        
        return foo;
    }
}
